/*
 *  Copyright (c) 2001 Sun Microsystems, Inc.  All rights
 *  reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *
 *  1. Redistributions of source code must retain the above copyright
 *  notice, this list of conditions and the following disclaimer.
 *
 *  2. Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in
 *  the documentation and/or other materials provided with the
 *  distribution.
 *
 *  3. The end-user documentation included with the redistribution,
 *  if any, must include the following acknowledgment:
 *  "This product includes software developed by the
 *  Sun Microsystems, Inc. for Project JXTA."
 *  Alternately, this acknowledgment may appear in the software itself,
 *  if and wherever such third-party acknowledgments normally appear.
 *
 *  4. The names "Sun", "Sun Microsystems, Inc.", "JXTA" and "Project JXTA" must
 *  not be used to endorse or promote products derived from this
 *  software without prior written permission. For written
 *  permission, please contact Project JXTA at http://www.jxta.org.
 *
 *  5. Products derived from this software may not be called "JXTA",
 *  nor may "JXTA" appear in their name, without prior written
 *  permission of Sun.
 *
 *  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 *  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 *  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 *  ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 *  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 *  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 *  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 *  SUCH DAMAGE.
 *
 *  ====================================================================
 *
 *  This software consists of voluntary contributions made by many
 *  individuals on behalf of Project JXTA.  For more
 *  information on Project JXTA, please see
 *  <http://www.jxta.org/>.
 *
 *  This license is based on the BSD license adopted by the Apache Foundation.
 *
 *  $Id: UUID.java,v 1.2 2005/05/12 16:20:10 hamada Exp $
 *
 */
package net.jxta.share;

import java.util.Random;
import java.security.SecureRandom;

/**
 *  UUID implementation based on a psuedo-random number generator.
 */
public class UUID implements Cloneable {
    private byte[] bytes;
    // UUID bytes

    // Default random number generator
    private static Random rand;
    // Lock object for random number generator
    private static Object randLock = new Object();


    /**
     *  Creates a new UUID using the default random number generator. If a
     *  default random number generator has not already been set then a new
     *  instance of SecureRandom will be used and set as the default.
     */
    public UUID() {
        if (rand == null) {
            synchronized (randLock) {
                if (rand == null) {
                    rand = new SecureRandom();
                }
            }
        }
        bytes = createBytes(rand);
    }


    /**
     *  Creates a new UUID using the specified pseudo-random number generator.
     *
     *@param  random  Description of the Parameter
     */
    public UUID(Random random) {
        bytes = createBytes(rand);
    }


    /**
     *  Creates a new UUID parse from the specified string.
     *
     *@param  s  Description of the Parameter
     */
    public UUID(String s) {
        bytes = parse(s);
    }


    /**
     *  Returns true if this UUID is equal to the specified object.
     *
     *@param  obj  Description of the Parameter
     *@return      Description of the Return Value
     */
    public boolean equals(Object obj) {
        if (!(obj instanceof UUID)) {
            return false;
        }
        byte[] b1 = this.bytes;
        byte[] b2 = ((UUID) obj).bytes;
        if (b1.length != b2.length) {
            return false;
        }
        for (int i = 0; i < b1.length; i++) {
            if (b1[i] != b2[i]) {
                return false;
            }
        }
        return true;
    }


    /**
     *  Returns the hash code value for this UUID.
     *
     *@return    Description of the Return Value
     */
    public int hashCode() {
        byte[] b = bytes;
        return (((b[12] & 0xff) << 24) |
                ((b[13] & 0xff) << 16) |
                ((b[14] & 0xff) << 8) |
                ((b[15] & 0xff) << 0));
    }


    /**
     *  Returns the string specification of this UUID.
     *
     *@return    Description of the Return Value
     */
    public String toString() {
        StringBuffer sb = new StringBuffer(36);
        appendHex(sb, bytes, 0, 4);
        // time_low
        sb.append('-');
        appendHex(sb, bytes, 4, 2);
        // time_mid
        sb.append('-');
        appendHex(sb, bytes, 6, 2);
        // time_high_and_version
        sb.append('-');
        appendHex(sb, bytes, 8, 2);
        // clock_seq_and_reserved clock_seq_low
        sb.append('-');
        appendHex(sb, bytes, 10, 6);
        // node
        return sb.toString();
    }


    /**
     *  Sets the default random number generator to use when creating new
     *  UUID's. This method must be called before any UUID's are created and may
     *  only be called once.
     *
     *@param  r  The new random value
     */
    public static void setRandom(Random r) {
        synchronized (randLock) {
            if (rand != null) {
                throw new IllegalStateException(
                        "Random number generator already defined");
            }
            rand = r;
        }
    }


    // Appends hex bytes to a string buffer
    /**
     *  Description of the Method
     *
     *@param  sb   Description of the Parameter
     *@param  b    Description of the Parameter
     *@param  off  Description of the Parameter
     *@param  len  Description of the Parameter
     */
    private static void appendHex(StringBuffer sb, byte[] b, int off, int len) {
        while (--len >= 0) {
            int n = b[off++] & 0xff;
            sb.append(Character.forDigit(n >> 4, 16));
            sb.append(Character.forDigit(n & 15, 16));
        }
    }


    // Creates UUID bytes using the specified random number generator
    /**
     *  Description of the Method
     *
     *@param  rand  Description of the Parameter
     *@return       Description of the Return Value
     */
    private static byte[] createBytes(Random rand) {
        // Generate random uuid bytes.
        byte[] b = new byte[16];
        rand.nextBytes(b);
        // Set bits 12-15 of time_hi_and_version to version 4 (indicates
        // that this UUID was generated from a random number).
        b[6] = (byte) ((b[6] & 0x0f) | 0x40);
        // Set bits 6 and 7 of clock_seq_hi_and_reserved to 0 and 1,
        // respectively (indicates IETF variant).
        b[8] = (byte) ((b[8] & 0x3f) | 0x80);
        return b;
    }


    // Parses UUID specification from a string
    /**
     *  Description of the Method
     *
     *@param  s  Description of the Parameter
     *@return    Description of the Return Value
     */
    private static byte[] parse(String s) {
        if (s.length() != 36 || s.charAt(8) != '-' || s.charAt(13) != '-'
                 || s.charAt(18) != '-' || s.charAt(23) != '-') {
            throw new IllegalArgumentException();
        }
        byte[] b = new byte[16];
        parseHex(s, 0, b, 0, 4);
        // time_low
        parseHex(s, 9, b, 4, 2);
        // time_mid
        parseHex(s, 14, b, 6, 2);
        // time_high_and_version
        parseHex(s, 19, b, 8, 2);
        // clock_seq_and_reserved clock_seq_low
        parseHex(s, 24, b, 10, 6);
        // node
        return b;
    }


    // Parses hex bytes from a string
    /**
     *  Description of the Method
     *
     *@param  s    Description of the Parameter
     *@param  i    Description of the Parameter
     *@param  b    Description of the Parameter
     *@param  off  Description of the Parameter
     *@param  len  Description of the Parameter
     */
    private static void parseHex(String s, int i, byte[] b, int off, int len) {
        while (--len >= 0) {
            int hi = Character.digit(s.charAt(i++), 16);
            int lo = Character.digit(s.charAt(i++), 16);
            if (hi == -1 || lo == -1) {
                throw new IllegalArgumentException();
            }
            b[off++] = (byte) (hi << 4 | lo);
        }
    }
}

